/**
 * @file    app_scene.c
 * @author  Azolla (1228449928@qq.com)
 * @brief   网关场景联动
 * @version v0.1
 * @date    2023-02-28
 * 
 * @copyright Copyright (c) 2023
 * */
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"

#include "cJSON.h"

#include "ble_mesh_prov.h"

// #define CONFIG_FATFS_FALSH     1        
#define CONFIG_FATFS_SD_TFT    1      

#ifdef CONFIG_FATFS_FALSH
#include "app_spiffs.h"
#define SCENE_ROOT_PATH   "/spiffs"      // 情景根目录路径
#endif
#ifdef CONFIG_FATFS_SD_TFT
#include "hal_usb_msc.h"
#define SCENE_ROOT_PATH   MOUNT_POINT"/scene"      // 情景根目录路径
#endif

#include "app_user.h"
#include "app_scene.h"

#define TAG  "app_scene"        // 注释掉，可关闭DEBUG信息
 
// 写情景 
void scene_write(const char *name, const char *ftext)
{
    char fpath[24]; // "/spiffs/1"
    sprintf(fpath, ""SCENE_ROOT_PATH"/%s", name); 
    #ifdef TAG
    ESP_LOGI(TAG, "write fpath: %s \n%s", fpath, ftext);
    #endif
    FILE *fb = fopen(fpath, "w+");  // 重新写入
    fwrite(ftext, sizeof(char), strlen(ftext), fb);
    fclose(fb);  // Remember to close file.
} 

// 读情景
int scene_read(const char *name, char *ftext)
{
    int fsize = 0;
    char fpath[24]; 
    sprintf(fpath, ""SCENE_ROOT_PATH"/%s", name); 
    #ifdef TAG
    ESP_LOGI(TAG, "read fpath: %s", fpath);
    #endif
    FILE *fb = fopen(fpath, "r");
    if (fb != NULL) {
        fsize = fread(ftext, sizeof(char), 1024, fb);
        fclose(fb);  // Remember to close file.
    }
    return fsize;
} 

static int scene_remove(const char *name)
{
    char fpath[24];  
    sprintf(fpath, ""SCENE_ROOT_PATH"/%s", name); 
    #ifdef TAG
    ESP_LOGI(TAG, "delete fpath: %s", fpath);
    #endif
    return remove(fpath);  // 删除文件
}

// 删除情景
int scene_delete(const char *name)
{
    if (strncmp(name, "all", 3) == 0) {  // "all"全删

        DIR *dir = NULL;  
        struct dirent *entry;  
        
        if ( (dir = opendir(SCENE_ROOT_PATH)) == NULL){  
            return 0;
        }

        while ( (entry = readdir(dir)) != NULL) {  
            if (entry->d_type != DT_REG) continue;
            char fname[4];
            strcpy(fname, entry->d_name);
            scene_remove(fname);
        }  
        closedir(dir);   
        return 0;  
    }  

    return scene_remove(name);
}

static void bed_handler(scene_msg_t msg)
{
    rf_motor_write(msg.value, 2);
}

// 开灯设备
static void led1_handler(scene_msg_t msg)
{
    for (uint8_t i = 0; i < msg.size; i++) {
        ble_mesh_onoff_set_unack(((ARRAY_TYPE *)msg.value)[i], true);
        vTaskDelay(200);
    }
}


// 窗帘[addr, cmd, data], 
typedef struct {
    uint8_t addr;
    uint8_t cmd[2];
} curtain_cmd_t;
// 支持多窗帘控制
static void curtain_handler(scene_msg_t msg)
{
    for (uint8_t i = 0; i < msg.size; i += sizeof(curtain_cmd_t)) {
        curtain_cmd_t *value = (curtain_cmd_t *)(msg.value + i);
        ble_mesh_send_vendor_message(value->addr, MESH_MODEL_OP_ATTR_SET_UNACK, value->cmd, 2);
    }
}

typedef struct {
    const char *name;
    void (*handler)(scene_msg_t msg);
} scene_handler_t;

static const scene_handler_t scene_handler_table[] = {
    { "bed",        bed_handler         },
    { "led1",       led1_handler        },
    { "curtain",    curtain_handler     },
};
#define SCENE_TABLE_SIZE    (sizeof(scene_handler_table) / sizeof(scene_handler_t))

// {
//     "addr":"0x0001",                // 网关地址
//     "mode1":{                       // 情景模式1
//         "bed":[85,85,238,238],      // 床
//         "led1":[5,6],               // 开灯设备
//         "led0":[7,8,9,10,11,12],    // 关灯设备
//         "curtain":[13,10,1],        // 窗帘[addr, cmd, data]
//     }
// } 
 
// 执行情景动作 
// name: 情景名
int app_scene_action(const char *name)
{
    char *ftext = heap_caps_malloc(1024, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    assert(ftext);
    int size = scene_read(name, ftext);
    if (size > 0) {
        ftext[size] = '\0';  // end mark.
        #ifdef TAG  // debug
        ESP_LOGI(TAG, "scene->ftext: %s | %d", ftext, size);
        #endif
        cJSON *jroot = cJSON_Parse((char *)ftext);
        if (jroot == NULL) {  // ERROR!!!
            #ifdef TAG  // debug
            ESP_LOGE(TAG, "scene_read_tigger cJSON_Parse error");
            #endif
            goto scene_end;
        }

        cJSON *mode_item = cJSON_GetObjectItem(jroot, name);
        if (mode_item == NULL) {
            #ifdef TAG  // debug
            ESP_LOGE(TAG, "mode_item error");
            #endif
            cJSON_Delete(jroot);
            goto scene_end;
        }
        uint8_t jSize = cJSON_GetArraySize(mode_item);
        #ifdef TAG 
        ESP_LOGI(TAG, "cJSON_GetArraySize = %d", jSize);
        #endif

        ble_mesh_onoff_set_unack(MESH_LIGHT_GROUP_ADDR, false);  // 先关所有灯光
    
        for (uint8_t size = 0; size < jSize; size++) {
            cJSON *item = cJSON_GetArrayItem(mode_item, size);  // 遍历
            if (cJSON_IsNull(item))  break; // 为空
            #ifdef TAG 
            char *data = cJSON_PrintUnformatted(item);
            ESP_LOGI(TAG, "mode->%s = %s", name, data);
            cJSON_free(data);
            #endif
            vTaskDelay(1000);  // 延时一下再发送需要亮的灯光！
            //===========================================================================
            for (uint8_t i = 0; i < SCENE_TABLE_SIZE; i++) {
                if (strcmp(item->string, scene_handler_table[i].name) == 0) {
                    switch (item->type) {   
                    case cJSON_String:
                        break;
                    case cJSON_Array: {
                        scene_msg_t msg;
                        msg.size  = cJSON_GetArraySize(item);
                        msg.value = (ARRAY_TYPE *)malloc(msg.size * sizeof(ARRAY_TYPE));
                        for (uint8_t i = 0; i < msg.size; i++) {
                            cJSON *aitem = cJSON_GetArrayItem(item, i);  // 取值
                            ((ARRAY_TYPE *)msg.value)[i] = aitem->valueint;
                        }
                        ARRAY_TYPE *value = (ARRAY_TYPE *)msg.value;
                        esp_log_buffer_hex(item->string, value, msg.size);
                        scene_handler_table[i].handler(msg);
                        free(msg.value);
                        break;
                    }
                    case cJSON_Object:  
                        break;
                    default:  // error type
                        break;
                    }    
                }
            }
        }
        cJSON_Delete(jroot);
    }
scene_end:
    free(ftext);
    return 0;
}


 
void app_scene_init(void)
{
#ifdef CONFIG_FATFS_FALSH    
    app_spiffs_init();
#endif    

#if 0    
    timer_trigger_t timer = { 0 };
    device_trigger_t device = { 0 };
    // clear zero...
    // memset(&timer,  0, sizeof(timer_trigger_t));
    // memset(&device, 0, sizeof(device_trigger_t));
    // 如果情景有更新，需要重新读条件触发条件！
    if ( scene_update_event() == true ) {
        scene_read_tigger(&timer, &device);
    }
 
    /* 情景之定时器触发 */
    for (uint8_t i = 0; i < timer.total; i++) {
        #ifdef TAG
        ESP_LOGI(TAG, "timer.event[%d].name = %s", i, timer.event[i].name);
        ESP_LOGI(TAG, "timer.event[%d].week = 0x%02x", i, timer.event[i].week);
        ESP_LOGI(TAG, "timer.event[%d].time = %d", i, timer.event[i].time);
        #endif
        struct tm *sntp = sntp_get_time();
        if (timer.event[i].week & (1 << sntp->tm_wday)) { // 先判断星期
            if (timer.event[i].time == sntp->tm_hour * 24 + sntp->tm_min) { // 24H制一天的分钟
                if (sntp->tm_sec == 0) { // 时间到了
                    // 触发情景
                }
            }
        }
    }

    /* 情景之设备状态触发 */
    for (uint8_t i = 0; i < device.total; i++) {
        #ifdef TAG
        ESP_LOGI(TAG, "device.event[%d].name = %s", i, device.event[i].name);
        ESP_LOGI(TAG, "device.event[%d].addr = "MACSTR"", i, MAC2STR(device.event[i].addr));
        ESP_LOGI(TAG, "device.event[%d].instruct = %s", i, device.event[i].instruct);
        #endif
        // if (memcmp(device.event[i].addr, addr, 6) == 0) {  // 先对比地址
        //     if (strcmp(device.event[i].instruct, data, len) == 0) { // 再对比指令字段
        //         // 触发情景
        //     }
        // }
    }

    app_scene_action(device.event[0].name);
#endif    
   
#if 0  // test...
    scene_write("1", "{\"addr\":\"94b555311dee\",\"onoff\":\"1\",\"level\":\"1\"}");
    char ftext[256];
    while (true) {
        int size = scene_read("1", ftext);
        if (size > 0) {
            ftext[size] = '\0';
            ESP_LOGI(TAG, "ftext: %s | %d", ftext, size);
        }
        vTaskDelay(3000);
    }
#endif    
}


